/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/***************************************************************************
 * Name: uds_dialer.cpp
 *
 * Purpose: implementation a dialer as client side connect to server side
 *
 * Developer:
 *   wen.gu , 2025-01-05
 *
 * TODO:
 *
 ***************************************************************************/

#include "udssipc/uds/uds_dialer.h"

#include "crt/osal/socket_uds.h"


#define CRT_LOG_TAG "UdsD"
#include "crt/log/log.h"


#define DIALER_CONNECT_INTERVAL_MS (40)

namespace udssipc::uds {

Dialer::Dialer(const std::string& url)
    :url_(url) {
    is_running_ = true;
    connect_worker_ = new std::thread([this](){
        while (this->is_running_){
            if (!this->request_connect_) {
                std::unique_lock<std::mutex> ul(lock_);
                this->cond_.wait(ul);

                if (!this->request_connect_){
                    continue;
                }
            }

            crt_socket_t sock_id = crt_socket_uds_open();
            if (sock_id != CRT_SOCKET_INVALID_SOCK) {
                int32_t ret = crt_socket_uds_connect(sock_id, this->url_.c_str(), DIALER_CONNECT_INTERVAL_MS, 0);
                if (ret == 0) {
                    this->connect_handler_(sock_id);
                    std::unique_lock<std::mutex> al(lock_);
                    this->request_connect_ = false;
                } else {
                    crt_socket_close(sock_id);
                }
            }
        }
    });
}

Dialer::~Dialer() {
    {
        std::unique_lock<std::mutex> al(lock_);
        is_running_ = false;
        request_connect_ = true;
        cond_.notify_all();
    }
    connect_worker_->join();
    delete connect_worker_;
}


/** sync connection */
core::UdsSocketId Dialer::connect(int32_t timeout_ms /* = core::Timeout::NoWait*/) {
    crt_socket_t sock_id = crt_socket_uds_open();
    if (sock_id != CRT_SOCKET_INVALID_SOCK) {
        uint32_t to_ms = 0;
        if (timeout_ms == core::Timeout::NoWait) {
            to_ms = 1; /** just 1 ms  as no wait mode */
        } else if (timeout_ms == core::Timeout::WaitForever) {
            to_ms = 0;
        } else {
            to_ms = timeout_ms;
        }

        int32_t ret = crt_socket_uds_connect(sock_id, url_.c_str(), to_ms, 0);
        if (ret == 0){
            return sock_id;
        } else {
            CRT_LOGE("connect to(%s) failed: %s\n", url_.c_str(), crt_socket_last_error_str());
            crt_socket_close(sock_id);
        }
    }

    return UDS_INVALID_SOCKET_ID;
}

/** async connection */
core::UdssErrC Dialer::connect(OnConnectHandler handler) {
    if (!handler) {
        return core::UdssErrC::InvalidArgument;
    }
    std::unique_lock<std::mutex> al(lock_);
    if (!request_connect_ && connect_worker_) {
        connect_handler_ = handler;
        request_connect_ = true;
        cond_.notify_one();

        return core::UdssErrC::OK;        
    }
    return core::UdssErrC::InvalidStatus;
}
core::UdssErrC Dialer::stop() {
    std::unique_lock<std::mutex> al(lock_);
    request_connect_ = false;
    return core::UdssErrC::OK;

}

} //namespace udssipc::uds